﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.Threading;
using System.Collections;
using System.Text.RegularExpressions;

namespace LPT_5
{
    public partial class Display : Form
    {
        private String msTweetText = null;
        private String msTweeter = null;
        private String msThisTag = "Please Wait...";
        private Twitter.TwitClient tcTwitter = null;
        private Flickr.FlickClient fcFlickr = null;
        private XmlDocument xnlTwitFeed = null;
        private ArrayList slTags = null;
        private FormManager.ExitCallback mcbExit = null;
        private Image mImag = null;
        private Thread tUpdateThread = null;

        private delegate void RefreshDelegate();

        public Display(String sUsername, String sPassword, FormManager.ExitCallback cbExit)
        {
            InitializeComponent();
            tcTwitter = new LPT_5.Twitter.TwitClient(sUsername, sPassword);
            fcFlickr = new LPT_5.Flickr.FlickClient();
            mcbExit = cbExit;
            slTags = new ArrayList();
        }

        private void ChangeDisplay()
        {
            lock(this)
            {
                //Nested loop - shouldnt be able to exit the loop until mImag has an image.
                //If socket returns null, the image was unavailable. If the inner loop exits
                //and mImag is still null, we tried every image and still found none so go to the next tag.
                Image iTempImg = null;
                do
                {
                    String[] sTweetDetails = null;
                    if (slTags.Count == 0)
                    {
                        sTweetDetails = GetRandomTwitterFeed();
                        slTags = GetUniqueTagValues(sTweetDetails[0]);
                    }
                    Random rRand = new Random();
                    int iRandTag = rRand.Next(slTags.Count);

                    XmlDocument xdDoc = fcFlickr.GetImagesByTag((String)slTags[iRandTag]);
                    
                    do
                    {
                        if (xdDoc["rsp"]["photos"] == null) break;
                        if (xdDoc["rsp"]["photos"].ChildNodes.Count == 0) break;
                        int iRandImg = rRand.Next(xdDoc["rsp"]["photos"].ChildNodes.Count);
                        XmlNode xnImageNode = xdDoc["rsp"]["photos"].ChildNodes[iRandImg];
                        xdDoc["rsp"]["photos"].RemoveChild(xnImageNode);
                        iTempImg = fcFlickr.ExtractImage(xnImageNode);
                    } while (iTempImg == null);

                    //Wait until after we have an image to set the text, otherwise if the
                    //form calls refresh on itself, the image and text will be wrong
                    msThisTag = (String)slTags[iRandTag];
                    slTags.RemoveAt(iRandTag);
                    slTags.TrimToSize();
                    if (sTweetDetails != null)
                    {
                        msTweetText = sTweetDetails[0];
                        msTweeter = sTweetDetails[1];
                    }

                } while (iTempImg == null);
                //Flickr dont give us images in 800x600, so we get a larger one and scale it down
                mImag = null;
                mImag = iTempImg.GetThumbnailImage(800, 600, null, IntPtr.Zero);
            }
            try
            {
                this.Invoke(new RefreshDelegate(Refresh));
            }
            catch (Exception e)
            {
            }
        }

        private String[] GetRandomTwitterFeed()
        {
            //Gets a list of top feeds then extracts and removes one at random.
            //When the list is empty, contact Twitter for a new list
            if(xnlTwitFeed==null||xnlTwitFeed["statuses"].ChildNodes.Count==0)
                xnlTwitFeed = tcTwitter.GetFriendsTimeline(40);

            if (xnlTwitFeed["statuses"] == null) return null;
            int iIndex = new Random().Next(xnlTwitFeed["statuses"].ChildNodes.Count-1);
            XmlNodeList xlItem = xnlTwitFeed["statuses"].ChildNodes;
            String sTweet = xlItem[iIndex]["text"].InnerText;
            String sName = xlItem[iIndex]["user"]["name"].InnerText;
            xnlTwitFeed["statuses"].RemoveChild(xlItem[iIndex]);

            return new String[]{sTweet,sName};
        }

        private void Display_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = this.CreateGraphics();

            //We use courier because it is monospaced, we can work
            //out the font size based on string length
            SizeF size = new SizeF();
            Brush bText = Brushes.AliceBlue;

            if (mImag != null)
                g.DrawImage(mImag,(Width/2)-(mImag.Width/2),(Height/2)-(mImag.Height/2));

            if (msTweetText != null)
            {
                Font fCourier = AppropriateFont(g, 5, 35, new Size(Width - 10, 50), msTweetText, new Font("Courier", 25), out size);
                g.DrawString(msTweetText, fCourier, bText, 0, 35);
            }

            if (msTweeter != null)
            {
                Font fCourier = new Font("Courier", 25);
                SizeF sSize = g.MeasureString(msTweeter, fCourier);
                g.DrawString(msTweeter, fCourier, bText, (Width / 2) - (sSize.Width / 2), 2);
            }

            //Draw the Tag text at the bottom centre. Leave a 5px gap between the text and bottom of window
            if (msThisTag != null)
            {
                Font fCourier = new Font("Courier", 25);
                SizeF sSize = g.MeasureString(msThisTag, fCourier);
                g.DrawString(msThisTag, fCourier, bText, (Width/2)-(sSize.Width/2), Height - (fCourier.Height * 2) - 5);
            }
        }

        private void tmrUpdate_Tick(object sender, EventArgs e)
        {
            //ChangeDisplay();
            
            //We have to store the new thread so that if the form is closed, we can call abort on it.
            tUpdateThread = new Thread(new ThreadStart(ChangeDisplay));
            tUpdateThread.IsBackground = true;
            tUpdateThread.Start();
            //Refresh();
        }

        private void Display_FormClosed(object sender, FormClosedEventArgs e)
        {
            if(mcbExit!=null)
            {
                tUpdateThread.Abort();
                mcbExit.Invoke();
            }
        }

        private ArrayList GetUniqueTagValues(String sTwitText)
        {
            String[] sTags = sTwitText.Split(' ');
            ArrayList slTags = new ArrayList();
            foreach (String sTag in sTags)
            {
                //We dont want any duplicates or very small words
                //Also remove anything that is non-alphanumeric
                if (!slTags.Contains(sTag)&&sTag.Length>4)
                    slTags.Add(Regex.Replace(sTag, @"\W*", ""));
            }

            return slTags;
        }

        //http://www.switchonthecode.com/tutorials/csharp-tutorial-font-scaling
        public static Font AppropriateFont(Graphics g, float minFontSize,
    float maxFontSize, Size layoutSize, string s, Font f, out SizeF extent)
        {
            if (maxFontSize == minFontSize)
                f = new Font(f.FontFamily, minFontSize, f.Style);

            extent = g.MeasureString(s, f);

            if (maxFontSize <= minFontSize)
                return f;

            float hRatio = layoutSize.Height / extent.Height;
            float wRatio = layoutSize.Width / extent.Width;
            float ratio = (hRatio < wRatio) ? hRatio : wRatio;

            float newSize = f.Size * ratio;

            if (newSize < minFontSize)
                newSize = minFontSize;
            else if (newSize > maxFontSize)
                newSize = maxFontSize;

            f = new Font(f.FontFamily, newSize, f.Style);
            extent = g.MeasureString(s, f);

            return f;
        }


    }
}
